iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
Web 3

以 Python 進入以太坊虛擬機 (EVM) 的幻想境界系列 第 14

虛擬境界 14:系統指令合約創建

  • 分享至 

  • xImage
  •  

第14章:系統指令合約創建 - CREATECREATE2

在 Ethereum 虛擬機 (EVM) 中,合約的創建是一個核心功能。這允許開發者部署新的智能合約到鏈上。EVM 提供了兩種主要的合約創建指令:CREATECREATE2。這兩者都允許創建新的合約,但它們的工作方式略有不同。

昨天解釋了簡單的系統指令的範例,今天就來實際實現 code

#
# System
#
CREATE = 0xF0
CREATE2 = 0xF5

CREATE 指令

CREATE 指令允許用戶創建新的智能合約。它使用發送者的地址和其當前的 nonce 來確定新合約的地址。當合約被創建時,它的初始化代碼會被執行,並且任何返回的代碼會被保存為合約的主體代碼。

def create(self):
        value = self.evm.stack.pop()
        mem_position = self.evm.stack.pop()
        length = self.evm.stack.pop()

        init_code = self.evm.memory[mem_position: mem_position + length]

        creator = self.getAccount(self.txn.origin)
        creator['balance'] -= value

        contract_addr_bytes = keccak(self.txn.thisAddr.encode() + str(creator['nonce']).encode()).digest()
        new_contract_address = '0x' + contract_addr_bytes[-20:].hex()

        creator['nonce'] += 1

        self.evm_account[new_contract_address] = {
            'balance': value,
            'nonce': 0,
            'storage': bytearray(),
            'code': init_code
        }

        self.evm.stack.append(int(new_contract_address, 16))

CREATE2 指令

CREATE2 指令是一個較新的添加,它允許用戶以一種可以預測的方式創建合約。這是通過使用發送者的地址、一個鹽值和初始化代碼的哈希來確定新合約的地址來實現的。這意味著,只要這些參數保持不變,新合約的地址就可以被預測。

在這邊先列出 create2 和 create 最大的不一致之處

init_code_hash = keccak(init_code).digest()
data_to_hash = b'\\xff' + self.txn.thisAddr.encode() + str(salt).encode() + init_code_hash
contract_addr_bytes = keccak(data_to_hash).digest()
new_contract_address = '0x' + contract_addr_bytes[-20:].hex()

在底下列出整個 CREATECREATE2 的詳細實現:

import random
from eth_hash.auto import keccak

class System:
    def __init__(self, evm, opCode, txn) -> None:
        self.evm = evm
        self.evm_account = {}
        self.txn = txn

    def create(self):
        value = self.evm.stack.pop()
        mem_position = self.evm.stack.pop()
        length = self.evm.stack.pop()

        init_code = self.evm.memory[mem_position: mem_position + length]

        creator = self.getAccount(self.txn.origin)
        creator['balance'] -= value

        contract_addr_bytes = keccak(self.txn.thisAddr.encode() + str(creator['nonce']).encode()).digest()
        new_contract_address = '0x' + contract_addr_bytes[-20:].hex()

        creator['nonce'] += 1

        self.evm_account[new_contract_address] = {
            'balance': value,
            'nonce': 0,
            'storage': bytearray(),
            'code': init_code
        }

        self.evm.stack.append(int(new_contract_address, 16))

    def create2(self, salt):
        value = self.evm.stack.pop()
        mem_position = self.evm.stack.pop()
        length = self.evm.stack.pop()

        init_code = self.evm.memory[mem_position: mem_position + length]

        creator = self.getAccount(self.txn.origin)
        creator['balance'] -= value

        init_code_hash = keccak(init_code).digest()
        data_to_hash = b'\\xff' + self.txn.thisAddr.encode() + str(salt).encode() + init_code_hash
        contract_addr_bytes = keccak(data_to_hash).digest()
        new_contract_address = '0x' + contract_addr_bytes[-20:].hex()

        creator['nonce'] += 1

        self.evm_account[new_contract_address] = {
            'balance': value,
            'nonce': 0,
            'storage': bytearray(),
            'code': init_code
        }

        self.evm.stack.append(int(new_contract_address, 16))

    def getAccount(self, address):
        if address not in self.evm_account:
            self.evm_account[address] = {
                'balance': random.randint(10, 50),
                'nonce': random.randint(0, 50),
            }
        return self.evm_account[address]

在這邊,因為是單純實現 evm create的部分,所以 account的部分就以空array 作弊過去哈哈,這邊大致介紹了系統指令的 create 和 create2 作法,如果有任何問題都可以提出問我。


上一篇
虛擬境界 13:系統指令
下一篇
虛擬境界 15:系統指令合約呼叫及回傳
系列文
以 Python 進入以太坊虛擬機 (EVM) 的幻想境界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言